#include "common.h"
#include "timer.h"
#include "uartDrive.h"
#include "uartProtocol.h"
#include "state.h"
#include "spiFlash.h"
#include "ArmUart.h"
#include "DutInfo.h"
#include "dutCtl.h"
#include "uartProtocolK71.h"

/******************************************************************************
* 【内部接口声明】
******************************************************************************/

// 数据结构初始化
void UART_PROTOCOL5_DataStructInit(UART_PROTOCOL5_CB *pCB);

// 一级接收缓冲区处理，从一级接收缓冲区中取出一个字节添加到命令帧缓冲区中
void UART_PROTOCOL5_RxFIFOProcess(UART_PROTOCOL5_CB *pCB);

// UART命令帧缓冲区处理
void UART_PROTOCOL5_CmdFrameProcess(UART_PROTOCOL5_CB *pCB);

// 对传入的命令帧进行校验，返回校验结果
BOOL UART_PROTOCOL5_CheckSUM(UART_PROTOCOL5_RX_CMD_FRAME *pCmdFrame);

// 将临时缓冲区添加到命令帧缓冲区中，其本质操作是承认临时缓冲区数据有效
BOOL UART_PROTOCOL5_ConfirmTempCmdFrameBuff(UART_PROTOCOL5_CB *pCB);

// 协议层发送处理过程
void UART_PROTOCOL5_TxStateProcess(void);

// UART协议层向驱动层注册数据发送接口
void UART_PROTOCOL5_RegisterDataSendService(BOOL (*service)(uint16 id, uint8 *pData, uint16 length));

//===============================================================================================================
// 发送命令带结果
void UART_PROTOCOL5_SendCmdWithResult(uint8 cmdWord, uint8 result);

// 发送命令带结果
void UART_PROTOCOL5_SendLdoV(uint8 cmdWord, uint32 result);

// 全局变量定义
UART_PROTOCOL5_CB uartProtocolCB5;

// ■■函数定义区■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 协议初始化
void UART_PROTOCOL5_Init(void)
{
    // 协议层数据结构初始化
    UART_PROTOCOL5_DataStructInit(&uartProtocolCB5);

    // 向驱动层注册数据接收接口
//  UART_DRIVE_RegisterDataSendService(UART_PROTOCOL5_MacProcess);

    // 向驱动层注册数据发送接口
    UART_PROTOCOL5_RegisterDataSendService(UART_DRIVE_AddTxArray);
}

// UART协议层过程处理
void UART_PROTOCOL5_Process(void)
{
    // UART接收FIFO缓冲区处理
    UART_PROTOCOL5_RxFIFOProcess(&uartProtocolCB5);

    // UART接收命令缓冲区处理
    UART_PROTOCOL5_CmdFrameProcess(&uartProtocolCB5);

    // UART协议层发送处理过程
    UART_PROTOCOL5_TxStateProcess();
}

// 向发送命令帧队列中添加数据
void UART_PROTOCOL5_TxAddData(uint8 data)
{
    uint16 head = uartProtocolCB5.tx.head;
    uint16 end =  uartProtocolCB5.tx.end;
    UART_PROTOCOL5_TX_CMD_FRAME *pCmdFrame = &uartProtocolCB5.tx.cmdQueue[uartProtocolCB5.tx.end];

    // 发送缓冲区已满，不予接收
    if ((end + 1) % UART_PROTOCOL5_TX_QUEUE_SIZE == head)
    {
        return;
    }

    // 队尾命令帧已满，退出
    if (pCmdFrame->length >= UART_PROTOCOL5_TX_CMD_FRAME_LENGTH_MAX)
    {
        return;
    }

    // 数据添加到帧末尾，并更新帧长度
    pCmdFrame->buff[pCmdFrame->length] = data;
    pCmdFrame->length ++;
}

// 确认添加命令帧，即承认之前填充的数据为命令帧，将其添加到发送队列中，由main进行调度发送，本函数内会自动校正命令长度，并添加校验码
void UART_PROTOCOL5_TxAddFrame(void)
{
    uint16 cc = 0;
    uint16 i = 0;
    uint16 head = uartProtocolCB5.tx.head;
    uint16 end  = uartProtocolCB5.tx.end;
    UART_PROTOCOL5_TX_CMD_FRAME *pCmdFrame = &uartProtocolCB5.tx.cmdQueue[uartProtocolCB5.tx.end];
    uint16 length = pCmdFrame->length;

    // 发送缓冲区已满，不予接收
    if ((end + 1) % UART_PROTOCOL5_TX_QUEUE_SIZE == head)
    {
        return;
    }

    // 命令帧长度不足，清除已填充的数据，退出
    if (UART_PROTOCOL5_CMD_FRAME_LENGTH_MIN - 1 > length) // 减去"校验和"1个字节
    {
        pCmdFrame->length = 0;

        return;
    }

    // 队尾命令帧已满，退出
    if (length >= UART_PROTOCOL5_TX_CMD_FRAME_LENGTH_MAX)
    {
        return;
    }

    // 重新设置数据长度，系统在准备数据时，填充的"数据长度"可以为任意值，并且不需要添加校验码，在这里重新设置为正确的值
    pCmdFrame->buff[UART_PROTOCOL5_CMD_LENGTH_INDEX] = length - 4;  // 重设数据长度

    for (i = 0; i < length; i++)
    {
        cc ^= pCmdFrame->buff[i];
    }
    pCmdFrame->buff[pCmdFrame->length++] = ~cc ;

    uartProtocolCB5.tx.end ++;
    uartProtocolCB5.tx.end %= UART_PROTOCOL5_TX_QUEUE_SIZE;
    //pCB->tx.cmdQueue[pCB->tx.end].length = 0;   //2015.12.2修改
}


// 数据结构初始化
void UART_PROTOCOL5_DataStructInit(UART_PROTOCOL5_CB *pCB)
{
    uint16 i;

    // 参数合法性检验
    if (NULL == pCB)
    {
        return;
    }

    pCB->tx.txBusy = FALSE;
    pCB->tx.index = 0;
    pCB->tx.head = 0;
    pCB->tx.end = 0;
    for (i = 0; i < UART_PROTOCOL5_TX_QUEUE_SIZE; i++)
    {
        pCB->tx.cmdQueue[i].length = 0;
    }

    pCB->rxFIFO.head = 0;
    pCB->rxFIFO.end = 0;
    pCB->rxFIFO.currentProcessIndex = 0;

    pCB->rx.head = 0;
    pCB->rx.end  = 0;
    for (i = 0; i < UART_PROTOCOL5_RX_QUEUE_SIZE; i++)
    {
        pCB->rx.cmdQueue[i].length = 0;
    }
}

// UART报文接收处理函数(注意根据具体模块修改)
void UART_PROTOCOL5_MacProcess(uint16 standarID, uint8 *pData, uint16 length)
{
    uint16 end = uartProtocolCB5.rxFIFO.end;
    uint16 head = uartProtocolCB5.rxFIFO.head;
    uint8 rxdata = 0x00;

    // 接收数据
    rxdata = *pData;

    // 一级缓冲区已满，不予接收
    if ((end + 1) % UART_PROTOCOL5_RX_FIFO_SIZE == head)
    {
        return;
    }
    // 一级缓冲区未满，接收
    else
    {
        // 将接收到的数据放到临时缓冲区中
        uartProtocolCB5.rxFIFO.buff[end] = rxdata;
        uartProtocolCB5.rxFIFO.end ++;
        uartProtocolCB5.rxFIFO.end %= UART_PROTOCOL5_RX_FIFO_SIZE;
    }
}

// UART协议层向驱动层注册数据发送接口
void UART_PROTOCOL5_RegisterDataSendService(BOOL (*service)(uint16 id, uint8 *pData, uint16 length))
{
    uartProtocolCB5.sendDataThrowService = service;
}

// 将临时缓冲区添加到命令帧缓冲区中，其本质操作是承认临时缓冲区数据有效
BOOL UART_PROTOCOL5_ConfirmTempCmdFrameBuff(UART_PROTOCOL5_CB *pCB)
{
    UART_PROTOCOL5_RX_CMD_FRAME *pCmdFrame = NULL;

    // 参数合法性检验
    if (NULL == pCB)
    {
        return FALSE;
    }

    // 临时缓冲区为空，不予添加
    pCmdFrame = &pCB->rx.cmdQueue[pCB->rx.end];
    if (0 == pCmdFrame->length)
    {
        return FALSE;
    }

    // 添加
    pCB->rx.end ++;
    pCB->rx.end %= UART_PROTOCOL5_RX_QUEUE_SIZE;
    pCB->rx.cmdQueue[pCB->rx.end].length = 0;   // 该行的作用是将新的添加位置有效数据个数清零，以便将这个位置当做临时帧缓冲区

    return TRUE;
}

// 协议层发送处理过程
void UART_PROTOCOL5_TxStateProcess(void)
{
    uint16 head = uartProtocolCB5.tx.head;
    uint16 end =  uartProtocolCB5.tx.end;
    uint16 length = uartProtocolCB5.tx.cmdQueue[head].length;
    uint8 *pCmd = uartProtocolCB5.tx.cmdQueue[head].buff;
    uint16 localDeviceID = uartProtocolCB5.tx.cmdQueue[head].deviceID;

    // 发送缓冲区为空，说明无数据
    if (head == end)
    {
        return;
    }

    // 发送函数没有注册直接返回
    if (NULL == uartProtocolCB5.sendDataThrowService)
    {
        return;
    }

    // 协议层有数据需要发送到驱动层
    if (!(*uartProtocolCB5.sendDataThrowService)(localDeviceID, pCmd, length))
    {
        return;
    }

    // 发送环形队列更新位置
    uartProtocolCB5.tx.cmdQueue[head].length = 0;
    uartProtocolCB5.tx.head ++;
    uartProtocolCB5.tx.head %= UART_PROTOCOL5_TX_QUEUE_SIZE;
}

// 一级接收缓冲区处理，从一级接收缓冲区中取出一个字节添加到命令帧缓冲区中
void UART_PROTOCOL5_RxFIFOProcess(UART_PROTOCOL5_CB *pCB)
{
    uint16 end = pCB->rxFIFO.end;
    uint16 head = pCB->rxFIFO.head;
    UART_PROTOCOL5_RX_CMD_FRAME *pCmdFrame = NULL;
    uint16 length = 0;
    uint8 currentData = 0;

    // 参数合法性检验
    if (NULL == pCB)
    {
        return;
    }

    // 一级缓冲区为空，退出
    if (head == end)
    {
        return;
    }

    // 获取临时缓冲区指针
    pCmdFrame = &pCB->rx.cmdQueue[pCB->rx.end];
    // 取出当前要处理的字节
    currentData = pCB->rxFIFO.buff[pCB->rxFIFO.currentProcessIndex];

    // 临时缓冲区长度为0时，搜索首字节
    if (0 == pCmdFrame->length)
    {
        // 命令头错误，删除当前字节并退出
        if (UART_PROTOCOL5_CMD_HEAD != currentData)
        {
            pCB->rxFIFO.head ++;
            pCB->rxFIFO.head %= UART_PROTOCOL5_RX_FIFO_SIZE;
            pCB->rxFIFO.currentProcessIndex = pCB->rxFIFO.head;

            return;
        }

        // 命令头正确，但无临时缓冲区可用，退出
        if ((pCB->rx.end + 1) % UART_PROTOCOL5_RX_QUEUE_SIZE == pCB->rx.head)
        {
            return;
        }

        // 添加UART通讯超时时间设置-2016.1.5增加
#if UART_PROTOCOL5_RX_TIME_OUT_CHECK_ENABLE
        TIMER_AddTask(TIMER_ID_UART_RX_TIME_OUT_CONTROL,
                      UART_PROTOCOL5_BUS_UNIDIRECTIONAL_TIME_OUT,
                      UART_PROTOCOL5_CALLBACK_RxTimeOut,
                      0,
                      1,
                      ACTION_MODE_ADD_TO_QUEUE);
#endif

        // 命令头正确，有临时缓冲区可用，则将其添加到命令帧临时缓冲区中
        pCmdFrame->buff[pCmdFrame->length++] = currentData;
        pCB->rxFIFO.currentProcessIndex ++;
        pCB->rxFIFO.currentProcessIndex %= UART_PROTOCOL5_RX_FIFO_SIZE;
    }
    // 非首字节，将数据添加到命令帧临时缓冲区中，但暂不删除当前数据
    else
    {
        // 临时缓冲区溢出，说明当前正在接收的命令帧是错误的，正确的命令帧不会出现长度溢出的情况
        if (pCmdFrame->length >= UART_PROTOCOL5_RX_CMD_FRAME_LENGTH_MAX)
        {
#if UART_PROTOCOL5_RX_TIME_OUT_CHECK_ENABLE
            // 停止RX通讯超时检测
            UART_PROTOCOL5_StopRxTimeOutCheck();
#endif

            // 校验失败，将命令帧长度清零，即认为抛弃该命令帧
            pCmdFrame->length = 0;  // 2016.1.5增加
            // 删除当前的命令头，而不是删除已分析完的所有数据，因为数据中可能会有命令头
            pCB->rxFIFO.head ++;
            pCB->rxFIFO.head %= UART_PROTOCOL5_RX_FIFO_SIZE;
            pCB->rxFIFO.currentProcessIndex = pCB->rxFIFO.head;

            return;
        }

        // 一直取到末尾
        while (end != pCB->rxFIFO.currentProcessIndex)
        {
            // 取出当前要处理的字节
            currentData = pCB->rxFIFO.buff[pCB->rxFIFO.currentProcessIndex];
            // 缓冲区未溢出，正常接收，将数据添加到临时缓冲区中
            pCmdFrame->buff[pCmdFrame->length++] = currentData;
            pCB->rxFIFO.currentProcessIndex ++;
            pCB->rxFIFO.currentProcessIndex %= UART_PROTOCOL5_RX_FIFO_SIZE;

            // ■■接下来，需要检查命令帧是否完整，如果完整，则将命令帧临时缓冲区扶正 ■■

            // 首先判断命令帧最小长度，一个完整的命令字至少包括4个字节: 命令帧最小长度，包含:命令头、命令字、数据长度、校验和，因此不足4个字节的必定不完整
            if (pCmdFrame->length < UART_PROTOCOL5_CMD_FRAME_LENGTH_MIN)
            {
                // 继续接收
                continue;
            }

            // 命令帧长度数值越界，说明当前命令帧错误，停止接收
            if (pCmdFrame->buff[UART_PROTOCOL5_CMD_LENGTH_INDEX] > (UART_PROTOCOL5_RX_CMD_FRAME_LENGTH_MAX - UART_PROTOCOL5_CMD_FRAME_LENGTH_MIN))
            {
#if UART_PROTOCOL5_RX_TIME_OUT_CHECK_ENABLE
                // 停止RX通讯超时检测
                UART_PROTOCOL5_StopRxTimeOutCheck();
#endif

                // 校验失败，将命令帧长度清零，即认为抛弃该命令帧
                pCmdFrame->length = 0;
                // 删除当前的命令头，而不是删除已分析完的所有数据，因为数据中可能会有命令头
                pCB->rxFIFO.head ++;
                pCB->rxFIFO.head %= UART_PROTOCOL5_RX_FIFO_SIZE;
                pCB->rxFIFO.currentProcessIndex = pCB->rxFIFO.head;

                return;
            }

            // 命令帧长度校验
            length = pCmdFrame->length;
            if (length < pCmdFrame->buff[UART_PROTOCOL5_CMD_LENGTH_INDEX] + UART_PROTOCOL5_CMD_FRAME_LENGTH_MIN)
            {
                // 长度要求不一致，说明未接收完毕，退出继续
                continue;
            }

            // 命令帧长度OK，则进行校验，失败时删除命令头
            if (!UART_PROTOCOL5_CheckSUM(pCmdFrame))
            {
#if UART_PROTOCOL5_RX_TIME_OUT_CHECK_ENABLE
                // 停止RX通讯超时检测
                UART_PROTOCOL5_StopRxTimeOutCheck();
#endif

                // 校验失败，将命令帧长度清零，即认为抛弃该命令帧
                pCmdFrame->length = 0;
                // 删除当前的命令头，而不是删除已分析完的所有数据，因为数据中可能会有命令头
                pCB->rxFIFO.head ++;
                pCB->rxFIFO.head %= UART_PROTOCOL5_RX_FIFO_SIZE;
                pCB->rxFIFO.currentProcessIndex = pCB->rxFIFO.head;

                return;
            }

#if UART_PROTOCOL5_RX_TIME_OUT_CHECK_ENABLE
            // 停止RX通讯超时检测
            UART_PROTOCOL5_StopRxTimeOutCheck();
#endif

            // 执行到这里，即说明接收到了一个完整并且正确的命令帧，此时需将处理过的数据从一级缓冲区中删除，并将该命令帧扶正
            pCB->rxFIFO.head += length;
            pCB->rxFIFO.head %= UART_PROTOCOL5_RX_FIFO_SIZE;
            pCB->rxFIFO.currentProcessIndex = pCB->rxFIFO.head;
            UART_PROTOCOL5_ConfirmTempCmdFrameBuff(pCB);

            return;
        }
    }
}

// 对传入的命令帧进行校验，返回校验结果
BOOL UART_PROTOCOL5_CheckSUM(UART_PROTOCOL5_RX_CMD_FRAME *pCmdFrame)
{
    uint8 cc = 0;
    uint16 i = 0;

    if (NULL == pCmdFrame)
    {
        return FALSE;
    }

    // 从命令头开始，到校验码之前的一个字节，依次相异或的运算结果取反
    for (i = 0; i < pCmdFrame->length - 1; i++)
    {
        cc ^= pCmdFrame->buff[i];
    }

    cc = ~cc;

    // 判断计算得到的校验码与命令帧中的校验码是否相同
    if (pCmdFrame->buff[pCmdFrame->length - 1] != cc)
    {
        return FALSE;
    }

    return TRUE;
}

// UART命令帧缓冲区处理
void UART_PROTOCOL5_CmdFrameProcess(UART_PROTOCOL5_CB *pCB)
{
    UART_PROTOCOL5_CMD cmd = UART_PROTOCOL5_CMD_NULL;
    UART_PROTOCOL5_RX_CMD_FRAME *pCmdFrame = NULL;

    uint8 eraResual = 0;

    // 参数合法性检验
    if (NULL == pCB)
    {
        return;
    }

    // 命令帧缓冲区为空，退出
    if (pCB->rx.head == pCB->rx.end)
    {
        return;
    }

    // 获取当前要处理的命令帧指针
    pCmdFrame = &pCB->rx.cmdQueue[pCB->rx.head];

    // 命令头非法，退出
    if (UART_PROTOCOL5_CMD_HEAD != pCmdFrame->buff[UART_PROTOCOL5_CMD_HEAD_INDEX])
    {
        // 删除命令帧
        pCB->rx.head ++;
        pCB->rx.head %= UART_PROTOCOL5_RX_QUEUE_SIZE;
        return;
    }

    // 命令头合法，则提取命令
    cmd = (UART_PROTOCOL5_CMD)pCmdFrame->buff[UART_PROTOCOL5_CMD_CMD_INDEX];

    // 执行命令帧
    switch (cmd)
    {
    // 空命令，不予执行
    case UART_PROTOCOL5_CMD_NULL:
        break;

    case UART_PROTOCOL5_CMD_ECO_APP_ERASE: // 0x25          //DUT_APP擦写结果查询
        TIMER_KillTask(TIMER_ID_STATE_CONTROL);

        eraResual = pCmdFrame->buff[UART_PROTOCOL5_CMD_DATA1_INDEX];
        // 擦除失败
        if (0 == eraResual)
        {
            // 擦除失败再擦除一次
            STATE_SwitchStep(STEP_K71_UART_UPGRADE_SEND_APP_EAR);
            break;
        }

        // 发送第一帧数据
        dut_info.currentAppSize = 0;
        STATE_SwitchStep(STEP_K71_UART_UPGRADE_SEND_APP_PACKET);
        break;

    case UART_PROTOCOL5_CMD_ECO_APP_WRITE: // 0x27      //DUT_APP写入结果
        eraResual = pCmdFrame->buff[UART_PROTOCOL5_CMD_DATA1_INDEX];

        if (0 == eraResual) // 如果仪表写入失败就退出
        {
            dut_info.currentAppSize--;
        }
        if (dut_info.currentAppSize < dut_info.appSize)
        {
            STATE_SwitchStep(STEP_K71_UART_UPGRADE_SEND_APP_PACKET);
        }
        else
        {
            STATE_SwitchStep(STEP_K71_UART_UPGRADE_APP_UP_SUCCESS);
        }
        break;

    case UART_PROTOCOL5_CMD_ECO_APP_WRITE_FINISH: // 0x2B           //DUT写入结果确定
        STATE_SwitchStep(STEP_K71_UART_UPGRADE_ITEM_FINISH);
        break;

    case UART_PROTOCOL5_CMD_ECO_JUMP_APP: // 0x2B           //DUT写入结果确定
        STATE_SwitchStep(STEP_K71_UART_UPGRADE_ITEM_FINISH);
        break;
        
    default:
        break;
    }

    // 删除命令帧
    pCB->rx.head ++;
    pCB->rx.head %= UART_PROTOCOL5_RX_QUEUE_SIZE;
}


// 上报命令字
void UART_PROTOCOL5_SendCmd(uint8 cmd)
{
    // 添加命令头
    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_HEAD);

    // 设备地址
    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_DUT_TO_KEY_DEVICE_ADDR);

    // 添加命令字
    UART_PROTOCOL5_TxAddData(cmd);

    // 添加数据长度
    UART_PROTOCOL5_TxAddData(0);

    // 添加检验和与结束符，并添加至发送
    UART_PROTOCOL5_TxAddFrame();
}


// 发送命令带结果
void UART_PROTOCOL5_SendCmdWithResult(uint8 cmdWord, uint8 result)
{
    // 添加命令头
    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_HEAD);

    // 设备地址
    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_DUT_TO_KEY_DEVICE_ADDR);

    // 添加命令字
    UART_PROTOCOL5_TxAddData(cmdWord);

    // 添加数据长度
    UART_PROTOCOL5_TxAddData(1);

    UART_PROTOCOL5_TxAddData(result);

    UART_PROTOCOL5_TxAddFrame();
}

// 发送K71 APP升级数据
void UART_PROTOCOL5_SendOnePacket(uint32 flashAddr, uint32 addr)
{
    uint8 addr1, addr2, addr3, addr4;
    uint8 appUpdateOnePacket[150] = {0};
    uint8 appUpdateOnePacket_i = 0;
    addr1 = (addr & 0xFF000000) >> 24;
    addr2 = (addr & 0x00FF0000) >> 16;
    addr3 = (addr & 0x0000FF00) >> 8;
    addr4 = (addr & 0x000000FF);
    SPI_FLASH_ReadArray(appUpdateOnePacket, flashAddr + addr, 128); // 工具读取128字节

    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_HEAD);
    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_DUT_TO_KEY_DEVICE_ADDR);

    UART_PROTOCOL5_TxAddData(UART_PROTOCOL5_CMD_ECO_APP_WRITE); // 命令0x06

    UART_PROTOCOL5_TxAddData(0x00);                                                    // 长度暂时为0
    UART_PROTOCOL5_TxAddData(addr1);                                                       // 添加地址
    UART_PROTOCOL5_TxAddData(addr2);                                                       // 添加地址
    UART_PROTOCOL5_TxAddData(addr3);                                                       // 添加地址
    UART_PROTOCOL5_TxAddData(addr4);                                                       // 添加地址
    for (appUpdateOnePacket_i = 0; appUpdateOnePacket_i < 128; appUpdateOnePacket_i++) // 添加数据
    {
        UART_PROTOCOL5_TxAddData(appUpdateOnePacket[appUpdateOnePacket_i]);
    }
    UART_PROTOCOL5_TxAddFrame(); // 调整帧格式,修改长度和添加校验
}
